package org.vectrics.skill;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import org.apache.log4j.Category;
import org.vectrics.SystemException;
import org.vectrics.VectricsException;
import org.vectrics.common.tree.TreeHelper;
import org.vectrics.common.util.ConfigurationReader;
import org.vectrics.domain.Domain;
import org.vectrics.hibernate.ThreadProperties;
import org.vectrics.recruiter.RecruitServiceLocator;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;



public class SkillConfig extends ConfigurationReader {
    private static Category log = Category.getInstance(SkillConfig.class);
    private static SkillConfig instance = null;
    private Skill rootSkill = null;
	private Skill dbRootSkill = null;
    private Domain domain = null;
	
    public SkillConfig(Domain domain) {
    	super();
    	this.domain = domain;
    	instance = this;
    	//this.setFileName("WEB-INF/skill-config.xml");
    }

    
    static public SkillConfig getInstance() throws VectricsException {
    	if (instance == null) {
    		log.error("SkillConfig not initialized on startup, make sure module is present in main recruiting config file.");
    	}
    	else {
    		throw new VectricsException("Skill Configuration not loaded - call parse(contents)");
    	}
    	return(instance);
    }
    

    protected void findSkillChildren(Skill parentSkill, Element parentElement) {
    	if (log.isDebugEnabled())
    		log.debug("SKILL: " + parentSkill.getCode() + " - finding children...");

    	String parentCode = parentSkill.getCode();
    	Skill dbParentSkill = (Skill)TreeHelper.findNode(dbRootSkill, parentCode);
	    Collection dbDeleteList = new ArrayList();
    	if (dbParentSkill != null) {
    	    dbDeleteList = dbParentSkill.getChildren();
    	}
    	
        NodeList children = parentElement.getChildNodes();
        if (log.isDebugEnabled())
        	log.debug("Element children = " + children.getLength());
        for (int i = 0; i < children.getLength(); i++) {
            Node node = children.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                if (node.getNodeName().equals("calculation")) {
                    log.debug("findSkillChildren() - calculation found");
                    Element calcElement = (Element)children.item(i);
                    String type= calcElement.getAttribute("type");
                    //String yesNo = calcElement.getAttribute("yes-no");
                    //String rating = calcElement.getAttribute("level");
                }
                
                if (node.getNodeName().equals("skill")) {
                    log.debug("loadSupportConfig() - skill element found.");
                    Element skillElement = (Element)children.item(i);
                    Skill newSkill = copyElementToSkill(skillElement);
                            
                    if (newSkill != null) {
        	            newSkill.setChildIndex(i);
                        parentSkill.addChild(newSkill);
                        newSkill.getScoringMethod().getDefaultScoringLevels(newSkill);
                        
                        RecruitServiceLocator.getSkillProvider().saveSkill(newSkill);
                        ThreadProperties.forcedCommit();
                       	
                        findSkillChildren(newSkill, skillElement);
                        
                        removeFromDeleteList(newSkill, dbDeleteList);
                    }
                    else {
                        log.error("Skills Configuration Error - name attribute missing.  Parent = " 
                        		+ parentCode);
                        throw new SystemException("Skills Configuration Error - name attribute missing.  Parent = " 
                        		+ parentCode);
                    }
                }
            }
        }
        
        Iterator iter = dbDeleteList.iterator();
        while (iter.hasNext()) {
            Skill skill = (Skill)iter.next();
            skill.setVoided(true);
            RecruitServiceLocator.getSkillProvider().updateSkill(skill);
            ThreadProperties.forcedCommit();
        }
    }

    /*
     * 
     */
    private Skill copyElementToSkill(Element skillElement) {
	    Skill newSkill = null;
	    String name = skillElement.getAttribute("name");
	    String code = skillElement.getAttribute("code");
	    String weight = skillElement.getAttribute("weight");
	    String conjunctionStr = skillElement.getAttribute("conjunction");
	    String section = skillElement.getAttribute("section");
	    String voided = skillElement.getAttribute("voided");
        String scoringMethodCode = skillElement.getAttribute("scoring");

        
    	SkillScoringMethod scoringMethod = null;
        if ((scoringMethodCode == null) || (scoringMethodCode.trim().length() == 0)) {
        	scoringMethod = RecruitServiceLocator.getSkillProvider().findSkillScoringMethod(SkillScoringMethod.SCORING_METHOD_NONE);
        } else {
        	scoringMethod = RecruitServiceLocator.getSkillProvider().findSkillScoringMethod(scoringMethodCode);
        	if (scoringMethod == null) {
        		throw new SystemException("Could not find scoring method for 'scoring' attribute, value = " + scoringMethod);
        	}
        }

        
	    if (name != null) {
	        if (code != null) {
	            newSkill = new Skill(); 
	            newSkill.setDomain(this.domain);
	            log.debug("findSkillChildren() - code = " + code);
                newSkill.setId(code);
	                
	            newSkill.setDescription(name);
	            
	            if (log.isDebugEnabled())
	                log.debug("findSkillChildren() - adding skill: " 
	                		+ newSkill.getId());

	            newSkill.setScoringMethod(scoringMethod);
	            
	            if ((weight == null) || (weight.length() == 0)) {
	            	weight = "100";
	            }
	            newSkill.setWeight(Long.parseLong(weight));
	            
	            if ((conjunctionStr == null) || (conjunctionStr.length() == 0)) {
	            	newSkill.setChildConjunctionType(Skill.CONJUNCTION_ADDITIVE);
	            } else if (conjunctionStr.toLowerCase().equals("maximum")) {
	            	newSkill.setChildConjunctionType(Skill.CONJUNCTION_MAXIMUM);
	            } else if (conjunctionStr.toLowerCase().equals("additive")) {
	            	newSkill.setChildConjunctionType(Skill.CONJUNCTION_ADDITIVE);
	            } else {
	            	throw new SystemException("Unexpected value for attribute 'conjunction' - value = " + conjunctionStr);
	            }
	            
	            if ((section != null) && (section.toLowerCase().startsWith("t"))) {
	            	newSkill.setSection(true);
	            } else {
	            	newSkill.setSection(false);
	            }
	            if ((newSkill.getCode() == null) || (newSkill.getCode().trim().length() == 0)) {
	                throw new SystemException("Skill Config file had element with null code,  name = " + newSkill.getDescription());
	            } else {
	                Skill otherWithSameCode = (Skill)TreeHelper.findNode(rootSkill, newSkill.getCode());
	                if (otherWithSameCode != null) {
	                    throw new SystemException("Code [" + newSkill.getCode() 
	                            + "] already used by other skill, this skill name = [" 
	                            + newSkill.getDescription() + "]  other = [" + otherWithSameCode.getDescription() + "]");
	                }
	            }
	            
	            if ((voided != null) && (voided.startsWith("t"))) {
	            	newSkill.setVoided(true);
	            } else {
	            	newSkill.setVoided(false);
	            }
	        } else {
		    	throw new SystemException("Skill does not have code: " + name);
	        }
	    } else {
	    	throw new SystemException("Skill does not have description: " + code);
	    }
	    return(newSkill);
    }
    
    
    private void removeFromDeleteList(Skill newSkill, Collection dbDeleteList) {
        Skill found = null;
        Iterator iter = dbDeleteList.iterator();
        while ((iter.hasNext()) && (found == null)) {
            Skill skill = (Skill)iter.next();
            if (skill.getCode().equals(newSkill.getCode())) {
                found = skill;
            }
        }
        if (found != null)
            dbDeleteList.remove(found);
    }
    
    protected void loadSkills(Element mainSection) {
        log.debug("loadSkills()");
        ThreadProperties.assertTransaction();

        try {
        	dbRootSkill = RecruitServiceLocator.getSkillProvider().findSkill("root");
        } catch (Exception ex) {
        	dbRootSkill = null;
        }
    	if (dbRootSkill == null) {
            ThreadProperties.assertTransaction();
            ThreadProperties.forcedCommit();

            ThreadProperties.assertTransaction();
	    	rootSkill = new Skill();
            rootSkill.setDomain(this.domain);
	    	rootSkill.setCode("root");
	    	rootSkill.setDescription("Root");
	    	rootSkill.setSection(false);
	    	SkillScoringMethod skillScoringMethod = RecruitServiceLocator.getSkillProvider().findSkillScoringMethod(SkillScoringMethod.SCORING_METHOD_YES_NO);
	    	rootSkill.setScoringMethod(skillScoringMethod);
	    	if (log.isDebugEnabled())
	    		log.debug("loadSkills() - root code = " + rootSkill.getId());
            rootSkill.getScoringMethod().getDefaultScoringLevels(rootSkill);
	    	
            ThreadProperties.assertTransaction();
	   		RecruitServiceLocator.getSkillProvider().saveSkill(rootSkill);
            ThreadProperties.forcedCommit();
	   		findSkillChildren(rootSkill, mainSection);
	   		
            ThreadProperties.assertTransaction();
	   		RecruitServiceLocator.getSkillProvider().updateSkill(rootSkill);
            ThreadProperties.forcedCommit();
	   		
            ThreadProperties.assertTransaction();
    	} else {
    		log.info("Root Skill Exists - Not Loading Configuration");
    	}
    }
    


    //  Read the XML configuratio file's elements into this class' variables.
    protected void load(Document document) {
       log.debug("load() - called");
       ThreadProperties.assertTransaction();
       Element mainSection = document.getDocumentElement();
       if (mainSection != null) {
            log.debug("load() - found main xml section");
            try {
            	loadSkills(mainSection);
            } catch (Exception ex) {
            	log.error("Exception loading skills from file: " + this.getFileName(), ex);
            }
        }
        else {
            log.warn("load() - could not find main section of config file.");
        }
    }


}